#ifdef COMPILE_OPENGL

#include "CBL_Debug.h"
#include "CBL_Math.h"
#include "COGL_PixelBufferAtomic.h"
#include <OpenGL/OpenGL.h>
#include <OpenGL/glext.h>

CB_SUBNAMESPACE_START(OpenGL)

#define ENUM_ITEM(_Name, _Size, _Components, _Flags, _OpenGLFormat, _D3D9Format, _D3D10Format) _OpenGLFormat,
static GLenum s_NativePixelBufferFormat[RenderLib::SPixelFormat::NumFormats] =
{
	0,
	#include "CRL_PixelBufferFormatsEnum.h"
};
#undef ENUM_ITEM

// -----------------------------------------------------------------------

#ifdef GL_VERSION_3_0
static GLenum s_NativeLockFlushType[RenderLib::IBufferAtomic::NumLockTypes] =
{
    GL_MAP_INVALIDATE_BUFFER_BIT,
    GL_MAP_INVALIDATE_RANGE_BIT,
    GL_MAP_READ,
    GL_MAP_READ | GL_MAP_INVALIDATE_RANGE_BIT,
};
#endif 

// -----------------------------------------------------------------------

static GLenum s_NativeLockAccessType[RenderLib::IBufferAtomic::NumLockTypes] =
{
	GL_WRITE_ONLY,
    GL_WRITE_ONLY,
    GL_READ_ONLY,
    GL_READ_WRITE
};

// -----------------------------------------------------------------------

static GLenum s_NativePixelBufferDimension[4] =
{
	GL_TEXTURE_1D,
	GL_TEXTURE_2D,
	GL_TEXTURE_3D,
    GL_TEXTURE_CUBE_MAP,
};

// -----------------------------------------------------------------------

CPixelBufferAtomic::CPixelBufferAtomic(const RenderLib::CPixelBufferDescriptor& _rDescriptor)
	: m_Descriptor(_rDescriptor)
    , m_BoundToStream(InvalidStream)
    , m_ResourceId(0)
    , m_BufferResourceId(0)
{	
    const GLclampf StaticPriority   = 1.0f;
    const GLclampf StandardPriority = 0.5f;
    const GLclampf DynamicPriority  = 0.0f;
    
	switch (m_Descriptor.GetStorageType())
    {
    case RenderLib::CPixelBufferDescriptor::RenderableTexture:
		glGenRenderbuffersEXT(1, &m_ResourceId);
		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_ResourceId);
        glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, 
                                 s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                 m_Descriptor.GetWidth(),
                                 m_Descriptor.GetHeight()); 
       break;
        
    case RenderLib::CPixelBufferDescriptor::StaticTexture:
    	glGenTextures(1, &m_ResourceId);
        glPrioritizeTextures(1, &m_ResourceId, &StaticPriority);
    	break;
    
    default:
    case RenderLib::CPixelBufferDescriptor::Texture:
    	glGenTextures(1, &m_ResourceId);
        glPrioritizeTextures(1, &m_ResourceId, &StandardPriority);
    	glGenBuffers(1, &m_BufferResourceId);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_STREAM_DRAW);
        break;
    
    case RenderLib::CPixelBufferDescriptor::DynamicTexture:
    	glGenTextures(1, &m_ResourceId);
        glPrioritizeTextures(1, &m_ResourceId, &DynamicPriority);
    	glGenBuffers(1, &m_BufferResourceId);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_DYNAMIC_DRAW);
        break;
    };
}

// -----------------------------------------------------------------------

CPixelBufferAtomic::~CPixelBufferAtomic()
{
	if (m_Descriptor.GetStorageType() == RenderLib::CPixelBufferDescriptor::RenderableTexture)
    {
        glDeleteRenderbuffersEXT(1, &m_ResourceId);
    }
    else
    {
    	glDeleteTextures(1, &m_ResourceId);
    	glDeleteBuffers(1, &m_BufferResourceId);
    }
}

// -----------------------------------------------------------------------

void* CPixelBufferAtomic::Lock(RenderLib::IBufferAtomic::ELockType _LockType)
{
	switch (m_Descriptor.GetStorageType())
    {
    case RenderLib::CPixelBufferDescriptor::Texture: 
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_BufferResourceId);
        
        if (_LockType != RenderLib::IBufferAtomic::LockOverwrite)
        	glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_STREAM_DRAW);
        break;
        
    case RenderLib::CPixelBufferDescriptor::DynamicTexture: 
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_BufferResourceId);
        
        if (_LockType == RenderLib::IBufferAtomic::LockOverwrite)
        	glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_DYNAMIC_DRAW);
        break;
    
    case RenderLib::CPixelBufferDescriptor::StaticTexture:
    	Debug::Error("Cannot lock static textures.");
    default:
    	return NULL;
    }
	
    return glMapBuffer(GL_PIXEL_UNPACK_BUFFER, s_NativeLockAccessType[_LockType]);
}

// -----------------------------------------------------------------------

void* CPixelBufferAtomic::Lock(RenderLib::IBufferAtomic::ELockType _LockType, const RenderLib::CRangeDescriptor& _rRange)
{
	Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffset() != RenderLib::CRangeDescriptor::InvalidValue,
                               "To partially lock a texture provide Offset"));
    Debug::Assert(CB_DEBUG_MSG(_rRange.GetCount() != RenderLib::CRangeDescriptor::InvalidValue,
                               "To partially lock a texture provide Count"));
    
	switch (m_Descriptor.GetStorageType())
    {
    case RenderLib::CPixelBufferDescriptor::Texture:
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_BufferResourceId);
        
        if (_LockType == RenderLib::IBufferAtomic::LockOverwrite)
            glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_STREAM_DRAW);
        break;
        
    case RenderLib::CPixelBufferDescriptor::DynamicTexture: 
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_BufferResourceId);
        
        if (_LockType == RenderLib::IBufferAtomic::LockOverwrite)
            glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_DYNAMIC_DRAW);
        break;
        
    case RenderLib::CPixelBufferDescriptor::StaticTexture:
        Debug::Error("Cannot rangelock static textures.");
    default:
        return NULL;
    }
    
	#ifdef GL_VERSION_3_0
    return glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, _rRange.GetOffset(), _rRange.GetCount(), s_NativeLockFlushType[_LockType]);
	#endif
    
    return (uint8*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, s_NativeLockAccessType[_LockType]) + _rRange.GetOffset();
}

// -----------------------------------------------------------------------

void CPixelBufferAtomic::Unlock()
{
	switch (m_Descriptor.GetStorageType())
    {
    case RenderLib::CPixelBufferDescriptor::StaticTexture:
        break;
    
    case RenderLib::CPixelBufferDescriptor::Texture: 
        glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
        break;
    
    case RenderLib::CPixelBufferDescriptor::DynamicTexture: 
        glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
        break;
    
    default:
    	break;
    }
}
// -----------------------------------------------------------------------

void CPixelBufferAtomic::Update(const void *_pData)
{
    if (Debug::Verify(CB_DEBUG_MSG(m_Descriptor.GetStorageType() != RenderLib::CPixelBufferDescriptor::RenderableTexture,
                                   "Cannot update renderable textures.")))
    {
        if (m_Descriptor.GetStorageType() == RenderLib::CPixelBufferDescriptor::StaticTexture)
        {
            TexImage(_pData);
        }
        else
        {
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_BufferResourceId);
            glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_DYNAMIC_DRAW);
            
            void* pBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
            memcpy(pBuffer, _pData, m_Descriptor.GetSizeByte());
            
            glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
        }
    }
}
    
// -----------------------------------------------------------------------

void CPixelBufferAtomic::TexImage(const void *_pData)
{
    glBindTexture(m_ResourceId, s_NativePixelBufferDimension[m_Descriptor.GetDimension()]);
    GLenum PixelDataType = (m_Descriptor.IsFloat()) ? GL_FLOAT : GL_UNSIGNED_BYTE;

	if ((RenderLib::CPixelBufferDescriptor::GetFormatInfo(m_Descriptor.GetFormat()).m_Flags & RenderLib::SPixelFlag::Compressed) == RenderLib::SPixelFlag::Compressed)
    {
    	switch (m_Descriptor.GetDimension())
        {
        case RenderLib::CPixelBufferDescriptor::PixelBuffer1D:
            glCompressedTexImage1D(	GL_TEXTURE_1D, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], m_Descriptor.GetWidth(), 0, 
                                    m_Descriptor.GetSizeByte(),
                                    (uint8*)_pData);
            break;
            
        default:
        case RenderLib::CPixelBufferDescriptor::PixelBuffer2D:
            glCompressedTexImage2D(	GL_TEXTURE_2D, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                   	m_Descriptor.GetSizeByte(),
                                   	(uint8*)_pData);
            break;
            
        case RenderLib::CPixelBufferDescriptor::PixelBuffer3D:
            glCompressedTexImage3D(	GL_TEXTURE_3D, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), m_Descriptor.GetDepth(), 0, 
                                   	m_Descriptor.GetSizeByte(),
                                   	(uint8*)_pData);
            break;
            
        case RenderLib::CPixelBufferDescriptor::PixelBufferCube:
            glCompressedTexImage2D(	GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                    m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                    m_Descriptor.GetSizeByte(),
                                   	(uint8*)_pData);
                                    
            glCompressedTexImage2D(	GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                    m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0,
                                    m_Descriptor.GetSizeByte(),
                                    (uint8*)_pData + m_Descriptor.GetSizeByte());
                                    
            glCompressedTexImage2D(	GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                    m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                    m_Descriptor.GetSizeByte(),
                                    (uint8*)_pData + m_Descriptor.GetSizeByte() * 2);
                                    
            glCompressedTexImage2D(	GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                    m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0,
                                    m_Descriptor.GetSizeByte(),
                                    (uint8*)_pData + m_Descriptor.GetSizeByte() * 3);
                                    
            glCompressedTexImage2D(	GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                    m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0,
                                    m_Descriptor.GetSizeByte(),
                                    (uint8*)_pData + m_Descriptor.GetSizeByte() * 4);
                                    
            glCompressedTexImage2D(	GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                    m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                    m_Descriptor.GetSizeByte(),
                                    (uint8*)_pData + m_Descriptor.GetSizeByte() * 5);
            break;
        };
    }
    else
    {
        switch (m_Descriptor.GetDimension())
        {
            case RenderLib::CPixelBufferDescriptor::PixelBuffer1D:
                glTexImage1D(	GL_TEXTURE_1D, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], m_Descriptor.GetWidth(), 0, 
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, _pData);
            break;
            
            default:
            case RenderLib::CPixelBufferDescriptor::PixelBuffer2D:
                glTexImage2D(	GL_TEXTURE_2D, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, _pData);
            break;
            
            case RenderLib::CPixelBufferDescriptor::PixelBuffer3D:
                glTexImage3D(	GL_TEXTURE_3D, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), m_Descriptor.GetDepth(), 0, 
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, _pData);
            break;
            
            case RenderLib::CPixelBufferDescriptor::PixelBufferCube:
                glTexImage2D(	GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, 
                                _pData);
            
                glTexImage2D(	GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0,
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, 
                                (uint8*)_pData + m_Descriptor.GetSizeByte());
            
                glTexImage2D(	GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, 
                                (uint8*)_pData + m_Descriptor.GetSizeByte() * 2);
            
                glTexImage2D(	GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0,
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, 
                                (uint8*)_pData + m_Descriptor.GetSizeByte() * 3);
            
                glTexImage2D(	GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0,
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, 
                                (uint8*)_pData + m_Descriptor.GetSizeByte() * 4);
            
                glTexImage2D(	GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                m_Descriptor.GetWidth(), m_Descriptor.GetHeight(), 0, 
                                s_NativePixelBufferFormat[m_Descriptor.GetFormat()], PixelDataType, 
                                (uint8*)_pData + m_Descriptor.GetSizeByte() * 5);
            break;
        };
    }
}

// -----------------------------------------------------------------------

void CPixelBufferAtomic::Update(const void *_pData, const RenderLib::CRangeDescriptor& _rRange)
{
	if (Debug::Verify(CB_DEBUG_MSG(m_Descriptor.GetStorageType() != RenderLib::CPixelBufferDescriptor::RenderableTexture,
                                   "Cannot update renderable textures.")))
    {
        if (m_Descriptor.GetStorageType() != RenderLib::CPixelBufferDescriptor::StaticTexture)
        {
            TexSubImage(_pData, _rRange);
        }
        else
        {
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_BufferResourceId);
            glBufferData(GL_PIXEL_UNPACK_BUFFER, m_Descriptor.GetSizeByte(), NULL, GL_DYNAMIC_DRAW);

    	#ifdef GL_VERSION_3_0
        
            //void* pBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
            //memcpy(pBuffer, _pData, _rRange.GetSizeX());
            
        #else	
            
            //void* pBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
            //memcpy(pBuffer + _Offset, _pData, _Size);
        
        #endif

        	glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
    	}
    }
}
    
// -----------------------------------------------------------------------

void CPixelBufferAtomic::TexSubImage(const void *_pData, const RenderLib::CRangeDescriptor& _rRange)
{
    glBindTexture(s_NativePixelBufferDimension[m_Descriptor.GetDimension()], m_ResourceId);
    GLenum PixelDataType = (m_Descriptor.IsFloat()) ? GL_FLOAT : GL_UNSIGNED_BYTE;

	#ifdef CB_DEBUG
    switch (m_Descriptor.GetDimension())
    {
        case RenderLib::CPixelBufferDescriptor::PixelBuffer1D:
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffsetX() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 1D Texture provide OffsetX"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetSizeX() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 1D Texture provide SizeX"));
        	break;
        
        default:
        case RenderLib::CPixelBufferDescriptor::PixelBuffer2D:
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffsetX() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 2D Texture provide OffsetX"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffsetY() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 2D Texture provide OffsetY"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetSizeX() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 2D Texture provide SizeX"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetSizeY() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 2D Texture provide SizeY"));
        	break;
        
        case RenderLib::CPixelBufferDescriptor::PixelBuffer3D:
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffsetX() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 3D Texture provide OffsetX"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffsetY() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 3D Texture provide OffsetY"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffsetZ() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 3D Texture provide OffsetZ"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetSizeX() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 3D Texture provide SizeX"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetSizeY() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 3D Texture provide SizeY"));
            Debug::Assert(CB_DEBUG_MSG(_rRange.GetSizeZ() != RenderLib::CRangeDescriptor::InvalidValue,
                                       "To partially update a 3D Texture provide SizeZ"));
        
        	break;
        
        case RenderLib::CPixelBufferDescriptor::PixelBufferCube:
        	Debug::Error("Partial update of cubemaps is currently not supported.");
        	return;
    };
    #endif
    
    if ((RenderLib::CPixelBufferDescriptor::GetFormatInfo(m_Descriptor.GetFormat()).m_Flags & RenderLib::SPixelFlag::Compressed) == RenderLib::SPixelFlag::Compressed)
    {
    	uint32 BytesPerPixel = RenderLib::CPixelBufferDescriptor::GetFormatInfo(m_Descriptor.GetFormat()).m_Size;
    
    	switch (m_Descriptor.GetDimension())
        {
        case RenderLib::CPixelBufferDescriptor::PixelBuffer1D:
        	glCompressedTexSubImage1D(	GL_TEXTURE_1D, 0, 
            							_rRange.GetOffsetX(), 
                                        _rRange.GetSizeX(), 
                                        s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                     	BytesPerPixel * _rRange.GetSizeX(), 
                                        _pData);
            break;
            
        default:
        case RenderLib::CPixelBufferDescriptor::PixelBuffer2D:
            glCompressedTexSubImage2D (	GL_TEXTURE_2D, 0,  
            							_rRange.GetOffsetX(), _rRange.GetOffsetY(), 
                                        _rRange.GetSizeX(), _rRange.GetSizeY(),
                                        s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                        BytesPerPixel * _rRange.GetSizeX() * _rRange.GetSizeY(),  
                                        _pData);
            break;
            
        case RenderLib::CPixelBufferDescriptor::PixelBuffer3D:
            glCompressedTexSubImage3D (	GL_TEXTURE_3D, 0, 
            							_rRange.GetOffsetX(), _rRange.GetOffsetY(), _rRange.GetOffsetZ(), 
            							_rRange.GetSizeX(), _rRange.GetSizeY(), _rRange.GetSizeZ(), 
                                        s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                                        BytesPerPixel * _rRange.GetSizeX() * _rRange.GetSizeY() * _rRange.GetSizeZ(), 
                                    	_pData);
            break;
            
        case RenderLib::CPixelBufferDescriptor::PixelBufferCube:
            Debug::Error("Partial update of cubemaps is currently not supported.");
            break;
        };
    }
    else
    {
    	switch (m_Descriptor.GetDimension())
        {
        case RenderLib::CPixelBufferDescriptor::PixelBuffer1D:
            glTexSubImage1D(GL_TEXTURE_1D, 0, 
            				_rRange.GetOffsetX(), 
                            _rRange.GetSizeX(), 
                            s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                            PixelDataType, _pData);
            break;
            
        default:
        case RenderLib::CPixelBufferDescriptor::PixelBuffer2D:
            glTexSubImage2D(GL_TEXTURE_2D, 0, 
            				_rRange.GetOffsetX(), _rRange.GetOffsetY(), 
                            _rRange.GetSizeX(), _rRange.GetSizeY(),
                            s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                            PixelDataType, _pData);
            break;
            
        case RenderLib::CPixelBufferDescriptor::PixelBuffer3D:
            glTexSubImage3D (GL_TEXTURE_3D, 0, 
                             _rRange.GetOffsetX(), _rRange.GetOffsetY(), _rRange.GetOffsetZ(), 
                             _rRange.GetSizeX(), _rRange.GetSizeY(), _rRange.GetSizeZ(), 
                             s_NativePixelBufferFormat[m_Descriptor.GetFormat()], 
                             PixelDataType, _pData);
            break;
            
        case RenderLib::CPixelBufferDescriptor::PixelBufferCube:
            Debug::Error("Partial update of cubemaps is currently not supported.");
            break;
        };
    }
}

// -----------------------------------------------------------------------

int CPixelBufferAtomic::Bind(uint8 _Stream, uint32 _Offset, uint32 _Size)
{
	if (Debug::Verify(CB_DEBUG_MSG(m_BoundToStream == InvalidStream, "Cannot bind a PixelBuffer to more than one stream at the same time.")))
    {
        switch (m_Descriptor.GetStorageType())
        {
        case RenderLib::CPixelBufferDescriptor::RenderableTexture:
            if ((m_Descriptor.GetFormat() == RenderLib::SPixelFormat::Depth_16) ||
                (m_Descriptor.GetFormat() == RenderLib::SPixelFormat::Depth_24) ||
                (m_Descriptor.GetFormat() == RenderLib::SPixelFormat::Depth_32))
            {
                m_BoundToStream = GL_DEPTH_ATTACHMENT_EXT;
            }
            else
            {
                GLint MaxAttachmentUnits;
                glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &MaxAttachmentUnits);
                
                Debug::Assert(	CB_DEBUG_MSG(_Stream < MaxAttachmentUnits, 
                                "Rendertarget cannot be bound to unit %d - only %d supported."),
                                _Stream, MaxAttachmentUnits);
                
                m_BoundToStream = GL_COLOR_ATTACHMENT0_EXT + std::min((GLint)_Stream, MaxAttachmentUnits);			
            }
                
            glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_ResourceId);
            glFramebufferRenderbufferEXT(GL_RENDERBUFFER_EXT, m_BoundToStream, GL_RENDERBUFFER_EXT, m_ResourceId);
            break;
            
        default:
        	m_BoundToStream = GL_TEXTURE0 + _Stream;
            
        	glActiveTexture(m_BoundToStream);
            glBindBuffer(s_NativePixelBufferDimension[m_Descriptor.GetDimension()], m_ResourceId);
            break;
        };
    }
    
    return glGetError();
}

// -----------------------------------------------------------------------

int CPixelBufferAtomic::Unbind()
{
	if (m_BoundToStream != InvalidStream)
    {
    	if (m_Descriptor.GetStorageType() == RenderLib::CPixelBufferDescriptor::RenderableTexture)
        {
        	glFramebufferRenderbufferEXT(GL_RENDERBUFFER_EXT, m_BoundToStream, GL_RENDERBUFFER_EXT, 0);
    	}
        else
        {
            glActiveTexture(m_BoundToStream);
        	glBindBuffer(s_NativePixelBufferDimension[m_Descriptor.GetDimension()], 0);
    	}
        
    	m_BoundToStream = InvalidStream;
    }
    
    return glGetError();
}

CB_SUBNAMESPACE_END

#endif